// 6.3.2 有返回值的函数
/**
 * 返回一个值的方式和初始化一个变量或形参的方式完全一样：返回的值用于初始化调用点的一个临时量，该临时量就是函数调用的结果。
 */
#include <iostream>
#include <initializer_list>
#include <iterator>
#include <vector>
using std::begin, std::end;
using std::cin, std::cout, std::endl;
using std::initializer_list;
using std::string;
using std::vector;

// 必须注意当函数返回局部变量时的初始化规则。
string make_plural(size_t ctr, const string &word, const string &ending)
{
    return (ctr > 1) ? (word + ending) : word;
}

// 同其他引用类型一样，如果函数返回引用，则该引用仅是它所引对象的一个别名。
// 举个例子来说明，假定某函数挑出两个string形参中较短的那个并返回其引用.
const string &shorterString(const string &s1, const string &s2)
{
    return s1.size() <= s2.size() ? s1 : s2;
}

// 不要返回局部对象的引用或指针
// 函数完成后，它所占用的存储空间也随之被释放掉（参见6.1.1节，第184页）。因此，函数终止意味着局部变量的引用将指向不再有效的内存区域：
// const string &manip()
// {
//     string ret;
//     // 以某种方式改变一下ret
//     if (!ret.empty())
//         return ret; // 错误，返回局部变量的引用
//     else
//         return "Empty"; // 错误，"Empty"是个局部临时量
// }

// 调用一个返回引用的函数得到左值，其他返回类型得到右值。
// 把函数调用放在赋值语句的左侧可能看起来有点奇怪，但其实这没什么特别的。
// 返回值是引用，因此调用是个左值，和其他左值一样它也能出现在赋值运算符的左侧。
char &get_val(string &str, string::size_type ix)
{
    return str[ix]; // 假设get_val的索引值是有效的（为了简写，不做校验了）
} // 但如果返回类型是常量引用，我们则不能给调用的结果赋值，这一点和我们熟悉的情况是一样的。

// 列表初始化返回值
vector<string> process()
{
    return {"a", "2", "c"};
}

// 递归：如果一个函数调用了它自身，不管这种调用是直接的还是间接的，都称该函数为递归函数（recursive function）
// 举个例子，我们可以使用递归函数重新实现求阶乘的功能
// 计算val的阶乘，即 1 * 2 * 3... * val
int factorial(int val)
{
    if (val > 1)
        return factorial(val - 1) * val;
    return 1; // 在递归函数中，一定有某条路径是不包含递归调用的；
              // 否则，函数将“永远”递归下去，换句话说，函数将不断地调用它自身直到程序栈空间耗尽为止。
}

int main(int argc, char const *argv[])
{
    string s("a value");
    cout << s << endl;   // 输出a value
    get_val(s, 0) = 'A'; // 将s[0]的值改为A。返回值是引用，因此调用是个左值，和其他左值一样它也能出现在赋值运算符的左侧
    cout << s << endl;   // 输出A value

    int i = factorial(3); // 调用递归函数
    cout << i << endl;

    return 0;
}
